{{>licenseInfo}}
{{#operations}}

#include "{{classname}}.h"
#include "IHttpBody.h"
#include "JsonBody.h"
#include "MultipartFormData.h"

#include <unordered_set>

#include <boost/algorithm/string/replace.hpp>

{{#apiNamespaceDeclarations}}
namespace {{this}} {
{{/apiNamespaceDeclarations}}

using namespace {{modelNamespace}};

{{classname}}::{{classname}}( std::shared_ptr<ApiClient> apiClient )
    : m_ApiClient(apiClient)
{
}

{{classname}}::~{{classname}}()
{
}

{{#operation}}
pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {{classname}}::{{operationId}}({{#allParams}}{{^required}}boost::optional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
{
{{#allParams}}{{#required}}{{^isPrimitiveType}}{{^isContainer}}
    // verify the required parameter '{{paramName}}' is set
    if ({{paramName}} == nullptr)
    {
        throw ApiException(400, utility::conversions::to_string_t("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}"));
    }
{{/isContainer}}{{/isPrimitiveType}}{{/required}}{{/allParams}}

    std::shared_ptr<ApiConfiguration> apiConfiguration( m_ApiClient->getConfiguration() );
    utility::string_t path = utility::conversions::to_string_t("{{{path}}}");
    {{#pathParams}}boost::replace_all(path, utility::conversions::to_string_t("{") + utility::conversions::to_string_t("{{baseName}}") + utility::conversions::to_string_t("}"), ApiClient::parameterToString({{{paramName}}}));
    {{/pathParams}}

    std::map<utility::string_t, utility::string_t> queryParams;
    std::map<utility::string_t, utility::string_t> headerParams( apiConfiguration->getDefaultHeaders() );
    std::map<utility::string_t, utility::string_t> formParams;
    std::map<utility::string_t, std::shared_ptr<HttpContent>> fileParams;

    std::unordered_set<utility::string_t> responseHttpContentTypes;
    {{#produces}}
    responseHttpContentTypes.insert( utility::conversions::to_string_t("{{{mediaType}}}") );
    {{/produces}}

    utility::string_t responseHttpContentType;

    // use JSON if possible
    if ( responseHttpContentTypes.size() == 0 )
    {
        {{#vendorExtensions.x-codegen-response.isString}}
        responseHttpContentType = utility::conversions::to_string_t("text/plain");
        {{/vendorExtensions.x-codegen-response.isString}}
        {{^vendorExtensions.x-codegen-response.isString}}
        responseHttpContentType = utility::conversions::to_string_t("application/json");
        {{/vendorExtensions.x-codegen-response.isString}}
    }
    // JSON
    else if ( responseHttpContentTypes.find(utility::conversions::to_string_t("application/json")) != responseHttpContentTypes.end() )
    {
        responseHttpContentType = utility::conversions::to_string_t("application/json");
    }
    // multipart formdata
    else if( responseHttpContentTypes.find(utility::conversions::to_string_t("multipart/form-data")) != responseHttpContentTypes.end() )
    {
        responseHttpContentType = utility::conversions::to_string_t("multipart/form-data");
    }
    {{#vendorExtensions.x-codegen-response.isString}}
    // plain text
    else if( responseHttpContentTypes.find(utility::conversions::to_string_t("text/plain")) != responseHttpContentTypes.end() )
    {
        responseHttpContentType = utility::conversions::to_string_t("text/plain");
    }
    {{/vendorExtensions.x-codegen-response.isString}}
    {{#vendorExtensions.x-codegen-response-ishttpcontent}}
    else
    {
        //It's going to be binary, so just use the first one.
        responseHttpContentType = *responseHttpContentTypes.begin();
    }
    {{/vendorExtensions.x-codegen-response-ishttpcontent}}
    {{^vendorExtensions.x-codegen-response-ishttpcontent}}
    else
    {
        throw ApiException(400, utility::conversions::to_string_t("{{classname}}->{{operationId}} does not produce any supported media type"));
    }
    {{/vendorExtensions.x-codegen-response-ishttpcontent}}

    headerParams[utility::conversions::to_string_t("Accept")] = responseHttpContentType;

    std::unordered_set<utility::string_t> consumeHttpContentTypes;
    {{#consumes}}
    consumeHttpContentTypes.insert( utility::conversions::to_string_t("{{{mediaType}}}") );
    {{/consumes}}

    {{#allParams}}
    {{^isBodyParam}}
    {{^isPathParam}}
    {{#required}}
        {{^isPrimitiveType}}
        {{^isContainer}}
    if ({{paramName}} != nullptr)
        {{/isContainer}}
        {{/isPrimitiveType}}
    {{/required}}
    {{^required}}
        {{^isPrimitiveType}}
        {{^isContainer}}
    if ({{paramName}} && *{{paramName}} != nullptr)
        {{/isContainer}}
        {{/isPrimitiveType}}
        {{#isPrimitiveType}}
    if ({{paramName}})
        {{/isPrimitiveType}}
        {{#isContainer}}
    if ({{paramName}})
        {{/isContainer}}
    {{/required}}
    {
        {{#isQueryParam}}
        queryParams[utility::conversions::to_string_t("{{baseName}}")] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}});
        {{/isQueryParam}}
        {{#isHeaderParam}}
        headerParams[utility::conversions::to_string_t("{{baseName}}")] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}});
        {{/isHeaderParam}}
        {{#isFormParam}}
        {{#isFile}}
        fileParams[ utility::conversions::to_string_t("{{baseName}}") ] = {{^required}}*{{/required}}{{paramName}};
        {{/isFile}}
        {{^isFile}}
        formParams[ utility::conversions::to_string_t("{{baseName}}") ] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}});
        {{/isFile}}
        {{/isFormParam}}
    }
    {{/isPathParam}}
    {{/isBodyParam}}
    {{/allParams}}

    std::shared_ptr<IHttpBody> httpBody;
    utility::string_t requestHttpContentType;

    // use JSON if possible
    if ( consumeHttpContentTypes.size() == 0 || consumeHttpContentTypes.find(utility::conversions::to_string_t("application/json")) != consumeHttpContentTypes.end() )
    {
        requestHttpContentType = utility::conversions::to_string_t("application/json");
        {{#bodyParam}}
        web::json::value json;

        {{#isPrimitiveType}}
        json = ModelBase::toJson({{paramName}});
        {{/isPrimitiveType}}
        {{^isPrimitiveType}}
        {{#isListContainer}}
        {
            std::vector<web::json::value> jsonArray;
            for( auto& item : {{paramName}} )
            {
                {{#items.isPrimitiveType}}jsonArray.push_back(ModelBase::toJson(item));
                {{/items.isPrimitiveType}}{{^items.isPrimitiveType}}{{#items.isString}}jsonArray.push_back(ModelBase::toJson(item));
                {{/items.isString}}{{^items.isString}}{{#items.isDateTime}}jsonArray.push_back(ModelBase::toJson(item));
                {{/items.isDateTime}}{{^items.isDateTime}}jsonArray.push_back( item.get() ? item->toJson() : web::json::value::null() );
                {{/items.isDateTime}}{{/items.isString}}{{/items.isPrimitiveType}}
            }
            json = web::json::value::array(jsonArray);
        }
        {{/isListContainer}}
        {{^isListContainer}}{{#required}}json = ModelBase::toJson({{paramName}});
        {{/required}}{{^required}}if ({{paramName}})
            json = ModelBase::toJson(*{{paramName}});{{/required}}
        {{/isListContainer}}
        {{/isPrimitiveType}}

        httpBody = std::shared_ptr<IHttpBody>( new JsonBody( json ) );
        {{/bodyParam}}
    }
    // multipart formdata
    else if( consumeHttpContentTypes.find(utility::conversions::to_string_t("multipart/form-data")) != consumeHttpContentTypes.end() )
    {
        requestHttpContentType = utility::conversions::to_string_t("multipart/form-data");
        {{#bodyParam}}
        std::shared_ptr<MultipartFormData> multipart(new MultipartFormData);
        {{#isPrimitiveType}}
        multipart->add(ModelBase::toHttpContent("{{paramName}}", {{paramName}}));
        {{/isPrimitiveType}}
        {{^isPrimitiveType}}
        {{#isListContainer}}
        {
            std::vector<web::json::value> jsonArray;
            for( auto& item : {{paramName}} )
            {
                {{#items.isPrimitiveType}}jsonArray.push_back(ModelBase::toJson(item));
                {{/items.isPrimitiveType}}{{^items.isPrimitiveType}}{{#items.isString}}jsonArray.push_back(ModelBase::toJson(item));
                {{/items.isString}}{{^items.isString}}{{#items.isDateTime}}jsonArray.push_back(ModelBase::toJson(item));
                {{/items.isDateTime}}{{^items.isDateTime}}jsonArray.push_back( item.get() ? item->toJson() : web::json::value::null() );
                {{/items.isDateTime}}{{/items.isString}}{{/items.isPrimitiveType}}
            }
            multipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), web::json::value::array(jsonArray), utility::conversions::to_string_t("application/json")));
        }{{/isListContainer}}
        {{^isListContainer}}{{#isString}}multipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), {{paramName}}));
        {{/isString}}{{^isString}}if({{^required}}{{paramName}} && (*{{paramName}}){{/required}}{{#required}}{{paramName}}{{/required}}.get())
        {
            {{^required}}(*{{/required}}{{paramName}}{{^required}}){{/required}}->toMultipart(multipart, utility::conversions::to_string_t("{{paramName}}"));
        }
        {{/isString}}
        {{/isListContainer}}
        {{/isPrimitiveType}}

        httpBody = multipart;
        requestHttpContentType += utility::conversions::to_string_t("; boundary=") + multipart->getBoundary();
        {{/bodyParam}}
    }
    else
    {
        throw ApiException(415, utility::conversions::to_string_t("{{classname}}->{{operationId}} does not consume any supported media type"));
    }

    {{#authMethods}}
    // authentication ({{name}}) required
    {{#isApiKey}}
    {{#isKeyInHeader}}
    {
        utility::string_t apiKey = apiConfiguration->getApiKey(utility::conversions::to_string_t("{{keyParamName}}"));
        if ( apiKey.size() > 0 )
        {
            headerParams[utility::conversions::to_string_t("{{keyParamName}}")] = apiKey;
        }
    }
    {{/isKeyInHeader}}
    {{#isKeyInQuery}}
    {
        utility::string_t apiKey = apiConfiguration->getApiKey(utility::conversions::to_string_t("{{keyParamName}}"));
        if ( apiKey.size() > 0 )
        {
            queryParams[utility::conversions::to_string_t("{{keyParamName}}")] = apiKey;
        }
    }
    {{/isKeyInQuery}}
    {{/isApiKey}}
    {{#isBasic}}
    // Basic authentication is added automatically as part of the http_client_config
    {{/isBasic}}
    {{#isOAuth}}
    // oauth2 authentication is added automatically as part of the http_client_config
    {{/isOAuth}}
    {{/authMethods}}

    return m_ApiClient->callApi(path, utility::conversions::to_string_t("{{httpMethod}}"), queryParams, httpBody, headerParams, formParams, fileParams, requestHttpContentType)
    .then([=](web::http::http_response response)
    {
        // 1xx - informational : OK
        // 2xx - successful       : OK
        // 3xx - redirection   : OK
        // 4xx - client error  : not OK
        // 5xx - client error  : not OK
        if (response.status_code() >= 400)
        {
            throw ApiException(response.status_code()
                , utility::conversions::to_string_t("error calling {{operationId}}: ") + response.reason_phrase()
                , std::make_shared<std::stringstream>(response.extract_utf8string(true).get()));
        }

        // check response content type
        if(response.headers().has(utility::conversions::to_string_t("Content-Type")))
        {
            utility::string_t contentType = response.headers()[utility::conversions::to_string_t("Content-Type")];
            if( contentType.find(responseHttpContentType) == std::string::npos )
            {
                throw ApiException(500
                    , utility::conversions::to_string_t("error calling {{operationId}}: unexpected response type: ") + contentType
                    , std::make_shared<std::stringstream>(response.extract_utf8string(true).get()));
            }
        }

        {{#vendorExtensions.x-codegen-response-ishttpcontent}}
        return response.extract_vector();
    })
    .then([=](std::vector<unsigned char> response)
    {
        HttpContent result;
        std::shared_ptr<std::stringstream> stream = std::make_shared<std::stringstream>(std::string(response.begin(), response.end()));
        result.setData(stream);
        return result;
        {{/vendorExtensions.x-codegen-response-ishttpcontent}}
        {{^vendorExtensions.x-codegen-response-ishttpcontent}}
        return response.extract_string();
    })
    .then([=](utility::string_t response)
    {
        {{^returnType}}
        return void();
        {{/returnType}}
        {{#returnType}}
        {{#returnContainer}}
        {{{returnType}}} result;
        {{/returnContainer}}
        {{^returnContainer}}
        {{{returnType}}} result({{{defaultResponse}}});
        {{/returnContainer}}

        if(responseHttpContentType == utility::conversions::to_string_t("application/json"))
        {
            web::json::value json = web::json::value::parse(response);

            {{#isListContainer}}for( auto& item : json.as_array() )
            {
                {{#vendorExtensions.x-codegen-response.items.isPrimitiveType}}result.push_back(ModelBase::{{vendorExtensions.x-codegen-response.items.datatype}}FromJson(item));
                {{/vendorExtensions.x-codegen-response.items.isPrimitiveType}}{{^vendorExtensions.x-codegen-response.items.isPrimitiveType}}{{#vendorExtensions.x-codegen-response.items.isString}}result.push_back(ModelBase::stringFromJson(item));
                {{/vendorExtensions.x-codegen-response.items.isString}}{{^vendorExtensions.x-codegen-response.items.isString}}{{{vendorExtensions.x-codegen-response.items.datatype}}} itemObj({{{vendorExtensions.x-codegen-response.items.defaultValue}}});
                itemObj->fromJson(item);
                result.push_back(itemObj);
                {{/vendorExtensions.x-codegen-response.items.isString}}{{/vendorExtensions.x-codegen-response.items.isPrimitiveType}}
            }
            {{/isListContainer}}{{^isListContainer}}{{#isMapContainer}}for( auto& item : json.as_object() )
            {
                {{#vendorExtensions.x-codegen-response.items.isPrimitiveType}}result[item.first] = ModelBase::{{vendorExtensions.x-codegen-response.items.datatype}}FromJson(item.second);
                {{/vendorExtensions.x-codegen-response.items.isPrimitiveType}}{{^vendorExtensions.x-codegen-response.items.isPrimitiveType}}{{#vendorExtensions.x-codegen-response.items.isString}}result[item.first] = ModelBase::stringFromJson(item.second);
                {{/vendorExtensions.x-codegen-response.items.isString}}{{^vendorExtensions.x-codegen-response.items.isString}}{{{vendorExtensions.x-codegen-response.items.datatype}}} itemObj({{{vendorExtensions.x-codegen-response.items.defaultValue}}});
                itemObj->fromJson(item.second);
                result[item.first] = itemObj;
                {{/vendorExtensions.x-codegen-response.items.isString}}{{/vendorExtensions.x-codegen-response.items.isPrimitiveType}}
            }
            {{/isMapContainer}}{{^isMapContainer}}{{#vendorExtensions.x-codegen-response.isPrimitiveType}}{{#vendorExtensions.x-codegen-response.items.datatype}}result = ModelBase::{{vendorExtensions.x-codegen-response.items.datatype}}FromJson(json);
            {{/vendorExtensions.x-codegen-response.items.datatype}}{{^vendorExtensions.x-codegen-response.items.datatype}}result = ModelBase::{{vendorExtensions.x-codegen-response.datatype}}FromJson(json);
            {{/vendorExtensions.x-codegen-response.items.datatype}}{{/vendorExtensions.x-codegen-response.isPrimitiveType}}{{^vendorExtensions.x-codegen-response.isPrimitiveType}}{{#vendorExtensions.x-codegen-response.isString}}result = ModelBase::stringFromJson(json);
            {{/vendorExtensions.x-codegen-response.isString}}{{^vendorExtensions.x-codegen-response.isString}}result->fromJson(json);{{/vendorExtensions.x-codegen-response.isString}}{{/vendorExtensions.x-codegen-response.isPrimitiveType}}{{/isMapContainer}}{{/isListContainer}}
        }{{#vendorExtensions.x-codegen-response.isString}}
        else if(responseHttpContentType == utility::conversions::to_string_t("text/plain"))
        {
            result = response;
        }{{/vendorExtensions.x-codegen-response.isString}}
        // else if(responseHttpContentType == utility::conversions::to_string_t("multipart/form-data"))
        // {
        // TODO multipart response parsing
        // }
        else
        {
            throw ApiException(500
                , utility::conversions::to_string_t("error calling {{operationId}}: unsupported response type"));
        }

        return result;
        {{/returnType}}
        {{/vendorExtensions.x-codegen-response-ishttpcontent}}
    });
}
{{/operation}}

{{#apiNamespaceDeclarations}}
}
{{/apiNamespaceDeclarations}}

{{/operations}}
